#include "cmd.h"
#include "cmsis_os.h"
#include "usart.h"
#include <string.h>
#include <stdlib.h>
#include "my_uart.h"

//定义串口3输出的信号量
osSemaphoreId_t output_semaphore;
char sprintf_buffer[100];//用于cmd任务中的sprintf的缓冲区
#define OUTPUT_BUFFER_SIZE 512  //输出缓冲区大小
uint8_t output_buffer[OUTPUT_BUFFER_SIZE]; //输出缓冲区

void output_str(const char* str,uint16_t len){
    osSemaphoreAcquire(output_semaphore, osWaitForever);
    memcpy(output_buffer,str,len>=OUTPUT_BUFFER_SIZE ? OUTPUT_BUFFER_SIZE:len);
    HAL_UART_Transmit_DMA(&huart3, output_buffer, len);
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart){
    if(huart->Instance == USART3){
        osSemaphoreRelease(output_semaphore);
    }
}

typedef struct{
    const char *name;//命令名称
    void (*func)(int argc, char** argv);
    const char *description;//命令描述
}Command;//命令结构体
static void cmd_help(int argc, char** argv);
static void cmd_clc(int argc,char** argv);
void cmd_baudRate(int argc, char** argv);
void cmd_output_str(int argc,char** argv);

const Command commands[] = {
    {"help", cmd_help,NULL},
    {"?", cmd_help,NULL},
    {"baudRate",cmd_baudRate,"设置或查看波特率,格式:\r\n \
        设置时:baudRate <串口号> <波特率> \r\n \
        查询时:baudRate <串口号>(可选)\
        参数说明: 串口号:1~2"},
    {"output",cmd_output_str,"输出字符串到控制台,格式:output <串口号> <功能1> <功能2> ...\r\n \
        参数说明:串口号:1~2 , 功能: \r\n \
        1)-time,用于输出时间戳\r\n \
        2)-num <数量>,用于控制输出多少数量的日志\r\n \
        3)-start <从第几条开始>,序号是从0开始算,用于选择开始位置 \r\n \
        4)-index,输出序号\r\n \
        5)-continue,继续上次输出的位置\r\n "},
    {"clc",cmd_clc,"打印换行来清屏"}
};

osThreadId_t cmdTaskHandle; //命令行解析处理的任务句柄
osSemaphoreId_t cmd_dataSemaphore; //命令行数据到达信号量

USART_Data uart3_data;//串口3接收数据结构体
uint8_t cmd_buffer[50];//命令行数据接收缓冲区
void dataProcess_taskFunc(void *argument);
/**
 * @brief 命令接收完成回调函数，在完成后释放信号量
 * 
 * @param me 
 */
void cmd_dataCpCallback(USART_Data* me){
    osSemaphoreRelease(cmd_dataSemaphore);
}
void cmd_init(){
    //控制端口串口发送互斥锁
    output_semaphore = osSemaphoreNew(1,1,NULL);
    //创建命令行数据到达信号量
    const osSemaphoreAttr_t cmd_dataSemaphoreAttr = {.name="cmd_dataSem",.cb_mem=NULL,.cb_size=0,.attr_bits=0};
    cmd_dataSemaphore = osSemaphoreNew(1,0,&cmd_dataSemaphoreAttr);
    //创建命令行任务
    const osThreadAttr_t cmdTaskAttr = {
        .name = "cmdTask",
        .stack_size = 256 * 4,
        .priority = (osPriority_t)osPriorityNormal,
    };
    cmdTaskHandle = osThreadNew(dataProcess_taskFunc,NULL,&cmdTaskAttr);
    if(cmdTaskHandle==NULL) HAL_UART_Transmit(&huart2,(uint8_t*)"cmdTaskHandleError\r\n",21,1000);
    //初始化命令行串口
    USART_DataTypeInit(&uart3_data, &huart3, cmd_buffer, sizeof(cmd_buffer), DMA_MODE, cmd_dataCpCallback);
}

char gl_argv[10][20];
char* argv[10];
/**
 * @brief 命令行处理任务函数
 * 
 * @param argument 
 * @note 该函数会阻塞等待信号量，信号量由串口接收完成中断回调函数释放
 *       收到信号量后会进行命令解析，并调用对应的命令处理函数
 */
void dataProcess_taskFunc(void *argument){
    int argc = 0;
    for(int i =0;i<10;i++){
        argv[i] = gl_argv[i];
    }
    // char** argv = (char**)pvPortMalloc(sizeof(char*)*10);
    for(;;){
        osSemaphoreAcquire(cmd_dataSemaphore, osWaitForever);
        uint16_t len = USART_DataGetReceivedLen(&uart3_data);
        uint8_t* data = USART_GetData(&uart3_data);
        uint8_t* WordStart = data;
        uint8_t* WordEnd = data;
        uint8_t parseState=0;//0:寻找单词开始 1:寻找单词结尾
        for(uint16_t i = 0; i < len; i++){
            if(parseState == 0){
                if(data[i] != ' ' && data[i] != '\r' && data[i] != '\n' && data[i] != '\t'){
                    WordStart = &data[i];
                    parseState = 1;
                }
            }
            if(parseState == 1){
                if(data[i] == ' ' || data[i] == '\r' || data[i] == '\n' || data[i] == '\t'||i==len-1){
                    if(i==(len-1)
                    &&(data[i] != ' ' && data[i] != '\r' && data[i] != '\n' && data[i] != '\t')){
                        WordEnd = &data[i]+1;
                    } 
                    else{
                        WordEnd=&data[i];
                    } 
                    parseState = 0;
                    argc++;
                    if(argc <= 10){
                        // argv[argc-1] = (char*)pvPortMalloc(WordEnd-WordStart+1);
                        memcpy(argv[argc-1], WordStart, (WordEnd-WordStart)>20?20:(WordEnd-WordStart));
                        argv[argc-1][(WordEnd-WordStart)>20?20:(WordEnd-WordStart)] = '\0';
                    }
                    else{
                        //参数过多，错误处理
                        output_str("参数过多\r\n",15);
                    }
                }
            }
        }
        if (argc > 0 && argc <= 10) {
          // 查找命令
          size_t i;
          for (i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) {
            if (strcmp(argv[0], commands[i].name) == 0) {
              commands[i].func(argc, argv);
              break;
            }
          }
          if(i==sizeof(commands) / sizeof(commands[0])){
              output_str("command not found\r\n",20);
          }
          for (int i = 0; i < argc; i++) {
            // vPortFree(argv[i]);
          }
          argc = 0;
        }
    }
}
static void cmd_help(int argc, char** argv){
    if(argc <= 1){
        for(size_t i=0; i<sizeof(commands)/sizeof(commands[0]); i++){
            output_str(commands[i].name, strlen(commands[i].name));
            if(commands[i].description){
                output_str(":", 1);
                output_str(commands[i].description, strlen(commands[i].description));
            }
            output_str("\r\n", 2);
        }
    }
    else{
        int tmp = argc;
        for(;tmp>1;tmp--){
            for(size_t i=0; i<sizeof(commands)/sizeof(commands[0]); i++){
                if(strcmp(argv[argc-tmp+1], commands[i].name) == 0){
                    output_str(commands[i].name, strlen(commands[i].name));
                    if(commands[i].description){
                        output_str(":", 1);
                        output_str(commands[i].description, strlen(commands[i].description));
                    }
                    output_str("\r\n", 2);
                    break;
                }
            }
        }
    }
}
void cmd_clc(int argc,char** argv){
    output_str("\r\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",40);
}